package net.oschina.allchat.server.framework.pipe.stream.negotiants;

import java.util.List;
import java.util.Locale;

import net.oschina.allchat.message.processing.IProcessingFactory;
import net.oschina.allchat.message.processing.ProcessingService;
import net.oschina.allchat.message.processing.translators.core.stream.FeaturesTranslatorFactory;
import net.oschina.allchat.message.processing.translators.core.stream.sasl.MechanismsTranslatorFactory;
import net.oschina.allchat.message.processing.translators.core.stream.tls.StartTlsTranslatorFactory;
import net.oschina.allchat.protocol.Constants;
import net.oschina.allchat.protocol.core.JabberId;
import net.oschina.allchat.protocol.core.ProtocolException;
import net.oschina.allchat.protocol.core.stream.Feature;
import net.oschina.allchat.protocol.core.stream.Features;
import net.oschina.allchat.protocol.core.stream.Stream;
import net.oschina.allchat.protocol.core.stream.error.InvalidFrom;
import net.oschina.allchat.protocol.core.stream.error.NotAuthorized;
import net.oschina.allchat.protocol.core.stream.error.ResourceConstraint;
import net.oschina.allchat.protocol.core.stream.error.UnsupportedVersion;
import net.oschina.allchat.protocol.core.stream.sasl.Mechanisms;
import net.oschina.allchat.protocol.core.stream.tls.StartTls;
import net.oschina.allchat.server.framework.core.integration.IMessage;
import net.oschina.allchat.server.framework.pipe.stream.IClientConnectionContext;
import net.oschina.allchat.server.framework.pipe.stream.StreamConstants;

public class InitialStreamNegotiant extends AbstractNegotiant {
	protected static IProcessingFactory processingFactory = ProcessingService.createStreamProcessingFactory();
	
	protected String domainName;
	protected List<Feature> features;
	
	static {
		processingFactory.register(Features.class, new FeaturesTranslatorFactory());
		processingFactory.register(StartTls.class, new StartTlsTranslatorFactory());
		processingFactory.register(Mechanisms.class, new MechanismsTranslatorFactory());
	}
	
	public InitialStreamNegotiant(String domainName, List<Feature> features) {
		this.domainName = domainName;
		this.features = features;
	}

	@Override
	protected boolean doNegotiate(IClientConnectionContext context, IMessage message) {
		Object obj = processingFactory.parse((String)message.getPayload());
		
		if (obj instanceof Stream) {
			Stream initialStream = (Stream)obj;
			
			Stream openStream = new Stream();
			// (rfc3290 4.7.1)
			// should provide the server's authoritative hostname
			openStream.setFrom(JabberId.parse(domainName));
			
			// (rfc3290 4.7.1)
            // must response a opening stream
			context.write(processingFactory.translate(openStream), true);
			
			if (initialStream.getVersion() != null && !Constants.SPECIFICATION_VERSION.equals(initialStream.getVersion())) {
				throw new ProtocolException(new UnsupportedVersion(String.format("unsupported specification version: %s",
						Constants.SPECIFICATION_VERSION)));
			}
			
			if (initialStream.getTo() != null) {
				if (!initialStream.getTo().toString().equals(domainName)) {
					throw new ProtocolException(new InvalidFrom(String.format("'to' attribute of 'stream' must be '%s'", domainName)));
				}
			}
			
			if (initialStream.getLang() != null) {
				boolean available = false;
				for (Locale locale : Locale.getAvailableLocales()) {
					if (locale.toString().equals(initialStream.getLang())) {
						available = true;
						break;
					}
				}
				
				if (!available) {
					throw new ProtocolException(new ResourceConstraint(String.format(
							"unspported lang: %s", initialStream.getLang())));
				}
				
				context.setAttribute(StreamConstants.KEY_STREAM_LANG, initialStream.getLang());
			}
			
			Features availableFeatures = new Features();
			availableFeatures.setFeatures(features);
			
			context.write(processingFactory.translate(availableFeatures));
			
			return true;
		} else {
			throw new ProtocolException(new NotAuthorized());
		}
	}

}
